package com.openluat.LuatTestServer;

import com.openluat.LuatTestServer.config.TestConfig;
import com.openluat.LuatTestServer.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

@SpringBootApplication
@EnableConfigurationProperties({TestConfig.class})
@Slf4j
public class LuatTestServerApplication {
    private static final String fileName = "/static/socket_test_file.txt";

    private static byte[] socketTestFileMD5;

    private static TestConfig config;

    @Autowired
    public void setConfig(TestConfig config) {
        LuatTestServerApplication.config = config;
    }

    static class SocketHandler extends Thread {
        private final Socket socket;

        public SocketHandler(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            InputStream input;
            OutputStream output;
            try {
                input = this.socket.getInputStream();
                output = this.socket.getOutputStream();
                if (input != null & output != null) {
                    handle(input, output);
                }
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }

        }

        private void handle(InputStream input, OutputStream output) throws IOException, InterruptedException {
            for (; ; ) {
                byte[] buff = new byte[1024];
                int count = input.read(buff);
                if (count == -1) {
                    log.info("tcpServer client " + socket.getInetAddress() + ":" + socket.getPort() + " disconnected");
                    input.close();
                    output.close();
                    break;
                }
                log.info("tcpServer receive data from " + socket.getInetAddress() + ":" + socket.getPort());
                log.info("data[" + count + "bytes]: " + new String(buff, 0, count, StandardCharsets.UTF_8));
                if (new String(buff, 0, count).equals("recv_big_file")) {
                    byte[] fileBuffer = new byte[1024];
                    InputStream fileInputStream = getClass().getResourceAsStream(fileName);
                    for (; ; ) {
                        int readCount = fileInputStream.read(fileBuffer);
                        if (readCount > 0) {
                            output.write(fileBuffer, 0, readCount);
                            Thread.sleep(100);
                        } else {
                            break;
                        }
                    }
                    fileInputStream.close();
                } else {
                    output.write(buff, 0, count);
                }
                output.flush();
            }
        }
    }

    static class UdpBigFileThread extends Thread {

        private DatagramPacket packet;
        private DatagramSocket ds;

        public UdpBigFileThread(DatagramPacket packet, DatagramSocket ds) {
            this.packet = packet;
            this.ds = ds;
        }

        @Override
        public void run() {
            packet.setData(socketTestFileMD5);
            try {
                ds.send(packet);
                byte[] fileBuffer = new byte[1024];
                InputStream fileReader = getClass().getResourceAsStream(fileName);
                while (true) {
                    int fileReadCount = fileReader.read(fileBuffer);
                    if (fileReadCount > 0) {
                        packet.setData(new String(fileBuffer, 0, fileReadCount).getBytes());
                        ds.send(packet);
                        Thread.sleep(100);
                    } else {
                        break;
                    }
                }
                fileReader.close();
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) {
        System.out.println("运行时环境版本:" + System.getProperty("java.version"));
        System.out.println("运行时环境供应商:" + System.getProperty("java.vendor"));
        System.out.println("Java 供应商的 URL:" + System.getProperty("java.vendor.url"));
        System.out.println("安装目录:" + System.getProperty("java.home"));
        System.out.println("Java 虚拟机规范版本:" + System.getProperty("java.vm.specification.version"));
        System.out.println("Java 虚拟机规范供应商:" + System.getProperty("java.vm.specification.vendor"));
        System.out.println("Java 运行时环境规范名称:" + System.getProperty("java.vm.specification.name"));
        System.out.println("Java 虚拟机实现版本:" + System.getProperty("java.vm.version"));
        System.out.println("Java 虚拟机实现供应商:" + System.getProperty("java.vm.vendor"));
        System.out.println("Java 虚拟机实现名称:" + System.getProperty("java.vm.name"));
        System.out.println("Java 运行时环境规范版本:" + System.getProperty("java.specification.version"));
        System.out.println("Java 运行时环境规范供应商:" + System.getProperty("java.specification.vendor"));
        System.out.println("Java 运行时环境规范名称:" + System.getProperty("java.specification.name"));
        System.out.println("Java 类格式版本号:" + System.getProperty("java.class.version"));
        System.out.println("Java 类路径:" + System.getProperty("java.class.path"));
        System.out.println("加载库时搜索的路径列表:" + System.getProperty("java.library.path"));
        System.out.println("默认的临时文件路径:" + System.getProperty("java.io.tmpdir"));
        System.out.println("要使用的 JIT 编译器的名称:" + System.getProperty("java.compiler"));
        System.out.println("一个或多个扩展目录的路径:" + System.getProperty("java.ext.dirs"));
        System.out.println("操作系统的名称:" + System.getProperty("os.name"));
        System.out.println("操作系统的架构:" + System.getProperty("os.arch"));
        System.out.println("操作系统的版本:" + System.getProperty("os.version"));
        System.out.println("文件分隔符（在 UNIX 系统中是“/”）:" + System.getProperty("file.separator"));
        System.out.println("路径分隔符（在 UNIX 系统中是“:”）:" + System.getProperty("path.separator"));
        System.out.println("行分隔符（在 UNIX 系统中是“/n”）:" + System.getProperty("line.separator"));
        System.out.println("用户的账户名称:" + System.getProperty("user.name"));
        System.out.println("用户的主目录:" + System.getProperty("user.home"));
        System.out.println("用户的当前工作目录:" + System.getProperty("user.dir"));

        SpringApplication.run(LuatTestServerApplication.class, args);

        socketTestFileMD5 = Utils.getFileStreamMD5(LuatTestServerApplication.class.getResourceAsStream(fileName)).getBytes();

        Boolean socketServerEnable = config.getSocketServerEnable();
        log.info("SocketServer Enable: " + socketServerEnable);
        if (socketServerEnable) {
            new Thread(() -> {
                try {
                    ServerSocket tcpServer = new ServerSocket(2901);
                    log.info("tcpServer start at 0.0.0.0:2901");
                    while (true) {
                        Socket socket = tcpServer.accept();
                        log.info("tcpServer accept connection from " + socket.getInetAddress() + ":" + socket.getPort());
                        Thread t = new SocketHandler(socket);
                        t.start();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();

            new Thread(() -> {
                try {
                    ServerSocket tcpServer = new ServerSocket(2902);
                    log.info("tcpDisconnectServer start at 0.0.0.0:2902");
                    while (true) {
                        Socket socket = tcpServer.accept();
                        log.info("tcpDisconnectServer accept connection from " + socket.getInetAddress() + ":" + socket.getPort());
                        socket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();

            new Thread(() -> {
                try {
                    DatagramSocket ds = new DatagramSocket(2901);
                    for (; ; ) {
                        byte[] buffer = new byte[1024];
                        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                        ds.receive(packet);
                        log.info("udpServer receive data from " + packet.getAddress() + ":" + packet.getPort());
                        String recvData = new String(packet.getData(), packet.getOffset(), packet.getLength());
                        log.info("data[" + packet.getLength() + "bytes]: " + recvData);
                        if (recvData.equals("big_data_md5_test")) {
                            UdpBigFileThread t = new UdpBigFileThread(packet, ds);
                            t.start();
                        } else {
                            ds.send(packet);
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }

}
